home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / stdwin / Ports / msdos / vtrm.c < prev   
Encoding:
C/C++ Source or Header  |  1991-04-23  |  23.6 KB  |  1,138 lines  |  [TEXT/????]

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1991. */
  2.  
  3. /*
  4.  * ibm Pc virtual TeRMinal package.
  5.  *
  6.  * (Under reconstruction by Guido!)
  7.  *
  8.  * An implementation of the VTRM interface for MS-DOS machines.
  9.  *
  10.  * This code supports two modes of accessing the screen.
  11.  * The first one (BIOS) will be used, unless the user overwrites this
  12.  * by setting the SCREEN environment variable.
  13.  * This variable can also be used to convey a screen size that differs
  14.  * from the default 25 lines and 80 columns. See below.
  15.  *
  16.  * The two modes are:
  17.  *
  18.  * 1) IBM BIOS interrupt 10 hex, video io.
  19.  *    (See IBM PC XT Technical Reference 6936833, May 1983,
  20.  *     Appendix A, pages A46-A47).
  21.  *    This is what you really want to use, since it's the only one that
  22.  *    can decently scroll. It cannot insert or delete characters, so
  23.  *    most optimisations from the unix version of vtrm.c are useless
  24.  *    and taken out.
  25.  *    Unfortunately, not every PC-compatible machine supports this BIOS
  26.  *    interrupt, so for these unlucky souls there is the following escape:
  27.  *
  28.  * 2) The ANSI.SYS driver.
  29.  *    (See IBM MS-DOS 6936839, Jan 1983, Version 2.00, Chapter 13.)
  30.  *    (Some compatibles don't have a separate ANSI.SYS driver but do the
  31.  *    same escape interpretation by default.)
  32.  *    This works reasonably, apart from scrolling downward, or part of
  33.  *    the screen, which is clumsy.
  34.  *    (The ANSI standard provides an escape sequence for scrolling
  35.  *    but ANSI.SYS does not support it, nor any other way of scrolling.)
  36.  *
  37.  * The rest of the interface is the same as described in the unix version,
  38.  * with the following exceptions:
  39.  *    - to ease coding for ansi scrolls, the terminal is supposed to
  40.  *    contain blanks at positions that were not written yet;
  41.  *    the unknown rubbish that is initially on the screen can
  42.  *    only be cleared by the caller by scrolling the whole screen up
  43.  *    by one or more lines;
  44.  *    - the number of lines on the terminal is assumed to be 25;
  45.  *    the number of columns is (1) determined by a BIOS function, or
  46.  *    (2) assumed to be 80 for ANSI;
  47.  *    the user can overwrite this by setting the environment variable:
  48.  *
  49.  *        SET SCREEN=BIOS y x
  50.  *    or
  51.  *        SET SCREEN=ANSI y x
  52.  *
  53.  *    where x and y are the number of lines and columns respectively.
  54.  *
  55.  * The lines and columns of our virtual terminal are numbered
  56.  *    y = {0...lines-1} from top to bottom, and
  57.  *    x = {0...cols-1} from left to right,
  58.  * respectively.
  59.  *
  60.  * The Visible Procedures in this package are as described in the unix version.
  61.  *
  62.  */
  63.  
  64. /* Includes: */
  65.  
  66. #include <stdio.h>
  67. #include <ctype.h>
  68. #include <string.h>
  69. #include <dos.h>
  70. #include <fcntl.h>
  71. #include <io.h>
  72. #include "vtrm.h"
  73.  
  74. /* Style definitions: */
  75.  
  76. #define Visible
  77. #define Hidden static
  78. #define Procedure
  79.  
  80. typedef int bool;
  81. typedef char *string;
  82. typedef short intlet;
  83.  
  84. #define Yes ((bool) 1)
  85. #define No  ((bool) 0)
  86.  
  87. /* Forward declarations: */
  88.  
  89. extern char *malloc();
  90.  
  91. /* Data definitions: */
  92.  
  93. #ifdef VTRMTRACE
  94. #define TRACEFILE "TRACE.TRM"
  95. Hidden FILE *vtrmfp= (FILE *) NULL;
  96. #endif
  97.  
  98. #define STDIN_HANDLE 0
  99.  
  100. #define Undefined (-1)
  101.  
  102. #define Min(a,b) ((a) <= (b) ? (a) : (b))
  103.  
  104. /* terminal status */
  105.  
  106. Hidden int started = No;
  107.  
  108. Hidden int scr_mode = 0;
  109. #define ANSI 'A'
  110. #define BIOS 'B'
  111.  
  112. #define Nlines    25
  113. #define Ncols    80
  114. Hidden int lines = Nlines;
  115. Hidden int cols = Ncols;
  116. Hidden int flags = 0;
  117.  
  118. /* current standout mode */
  119. #define Off    PLAIN
  120. #define On    STANDOUT
  121. Hidden int so_mode = Off;
  122.  
  123. /* current cursor position */
  124. Hidden intlet cur_y = Undefined, cur_x = Undefined;
  125.  
  126. /* "linedata[y][x]" holds the char on the terminal,
  127.  * "linemode[y][x]" the STANDOUT bit.
  128.  * The STANDOUT bit tells whether the character is standing out.
  129.  * "lenline[y]" holds the length of the line.
  130.  * (Partially) empty lines are distinghuished by "lenline[y] < cols".
  131.  * Unknown chars will be ' ', so the scrolling routines for ANSI
  132.  * can use "unwritten" chars (with indent > 0 in trmputdata).
  133.  * To make the optimising compare in putline fail, lenline[y] is initially 0.
  134.  * The latter implies that if a line is first addressed with trmputdata,
  135.  * any rubbish that is on the screen beyond the data that gets put, will
  136.  * remain there.
  137.  */
  138.  
  139. Hidden char     **linedata = 0;
  140. Hidden char     **linemode = 0;
  141.  
  142. Hidden intlet *lenline = 0;
  143.  
  144. /* To compare the mode part of the line when the
  145.  * mode parameter of trmputdata == NULL, we use the following:
  146.  */
  147. Hidden char plain[1]= {PLAIN};
  148.  
  149. /* Make the cursor invisible when trmsync() tries to move outside the screen */
  150. Hidden bool no_cursor = No;
  151.  
  152. /*
  153.  * Starting, Ending and (fatal) Error.
  154.  */
  155.  
  156. Hidden bool wasbreak;
  157.  
  158. /*
  159.  * Initialization call.
  160.  * Determine terminal mode.
  161.  * Start up terminal and internal administration.
  162.  * Return Yes if succeeded, No if trouble (which doesn't apply here).
  163.  */
  164.  
  165. Visible int trmstart(plines, pcols, pflags)
  166.      int *plines;
  167.      int *pcols;
  168.      int *pflags;
  169. {
  170.     static char setup = No;
  171.     int err;
  172.  
  173. #ifdef VTRMTRACE
  174.     if (!started) vtrmfp= fopen(TRACEFILE, vtrmfp ? "a" : "w");
  175.     if (vtrmfp) fprintf(vtrmfp, "\ttrmstart(&li, &co, &fl);\n");
  176. #endif
  177.  
  178.     if (started)
  179.         return TE_TWICE;
  180.  
  181.     if (!setup) {
  182.         err = set_screen_up();
  183.         if (err != TE_OK)
  184.             return err;
  185.         setup = Yes;
  186.     }
  187.  
  188.     err = start_trm();        /* internal administration */
  189.     if (err != TE_OK)
  190.         return err;
  191.  
  192.     *plines = lines;
  193.     *pcols = cols;
  194.     *pflags = flags;
  195.  
  196.     setmode(STDIN_HANDLE, O_BINARY); /* Don't translate CRLF to LF */
  197.     setraw(STDIN_HANDLE, Yes);
  198.     wasbreak = getbreak(); /* Save BREAK status; restore when done */
  199.     setbreak(No);
  200.  
  201.     started = Yes;
  202.     return TE_OK;
  203. }
  204.  
  205. Hidden int set_screen_up()
  206. {
  207.     int height;
  208.     int width;
  209.     int get_screen_env();
  210.     int get_cols();
  211.  
  212.     height = width = 0;
  213.     scr_mode = get_screen_env(&height, &width);
  214.  
  215.     switch (scr_mode) {
  216.     case BIOS:
  217.     case TE_OK:
  218.         cols = get_cols();
  219.         flags = HAS_STANDOUT|CAN_SCROLL;
  220.         break;
  221.     case ANSI:
  222.         flags = HAS_STANDOUT;
  223.         break;
  224.     default:
  225.         return scr_mode; /* Error flag */
  226.     }
  227.  
  228.     /* allow x and y in environment variable SCREEN to override */
  229.     if (height > 0)
  230.         lines = height;
  231.     if (width > 0)
  232.         cols = width;
  233.     return TE_OK;
  234. }
  235.  
  236. Hidden int get_screen_env(pheight, pwidth)
  237.      int *pheight;
  238.      int *pwidth;
  239. {
  240.     string s;
  241.     int mode;
  242.     char screrr;
  243.     string getenv();
  244.     string strip();
  245.     string skip();
  246.  
  247.     screrr = No;
  248.     s = getenv("SCREEN");
  249.     if (s == NULL)
  250.         return BIOS;
  251.  
  252.     s = strip(s);
  253.     switch (*s) {
  254.     case '\0':
  255.         return BIOS;
  256.     case 'a':
  257.         mode = ANSI;
  258.         s = skip(s, "ansi");
  259.         break;
  260.     case 'A':
  261.         mode = ANSI;
  262.         s = skip(s, "ANSI");
  263.         break;
  264.     case 'b':
  265.         mode = BIOS;
  266.         s = skip(s, "bios");
  267.         break;
  268.     case 'B':
  269.         mode = BIOS;
  270.         s = skip(s, "BIOS");
  271.         break;
  272.     default:
  273.         mode = BIOS;
  274.         screrr = Yes;
  275.     }
  276.  
  277.     /* *pheight and *pwidth were set to 0 above */
  278.     s = strip(s);
  279.     while (isdigit(*s)) {
  280.         *pheight = *pheight * 10 + (*s++ - '0');
  281.     }
  282.     s = strip(s);
  283.     while (isdigit(*s)) {
  284.         *pwidth = *pwidth * 10 + (*s++ -'0');
  285.     }
  286.     s = strip(s);
  287.     if (screrr || *s != '\0')
  288.         return TE_BADSCREEN;
  289.  
  290.     return mode;
  291. }
  292.  
  293. Hidden string strip(s)
  294.      string s;
  295. {
  296.     while (*s == ' ' || *s == '\t')
  297.         ++s;
  298.     return s;
  299. }
  300.  
  301. Hidden string skip(s, pat)
  302.      string s;
  303.      string pat;
  304. {
  305.     while (*s && *s == *pat)
  306.         ++s, ++pat;
  307.     return s;
  308. }
  309.  
  310. /* initialise internal administration */
  311.  
  312. Hidden int start_trm()
  313. {
  314.     register int y;
  315.  
  316.     if (linedata == NULL) {
  317.         if ((linedata = (char**) malloc(lines * sizeof(char*))) == NULL)
  318.             return TE_NOMEM;
  319.         for (y = 0; y < lines; y++) {
  320.             if ((linedata[y] = (char*) malloc(cols * sizeof(char))) == NULL)
  321.                 return TE_NOMEM;
  322.         }
  323.     }
  324.     if (linemode == NULL) {
  325.         if ((linemode = (char**) malloc(lines * sizeof(char*))) == NULL)
  326.             return TE_NOMEM;
  327.         for (y = 0; y < lines; y++) {
  328.             if ((linemode[y] = (char*) malloc(cols * sizeof(char))) == NULL)
  329.                 return TE_NOMEM;
  330.         }
  331.     }
  332.     if (lenline == 0) {
  333.         if ((lenline = (intlet *) malloc(lines * sizeof(intlet))) == NULL)
  334.             return TE_NOMEM;
  335.     }
  336.         
  337.     trmundefined();
  338.     return TE_OK;
  339. }
  340.  
  341. /*
  342.  * Termination call.
  343.  * Beware that it might be called by a catched interrupt even in the middle
  344.  * of trmstart()!
  345.  */
  346.  
  347. Visible Procedure trmend()
  348. {
  349. #ifdef VTRMTRACE
  350.     if (vtrmfp) fprintf(vtrmfp, "\ttrmend();\n");
  351. #endif
  352.     if (started && so_mode != Off)
  353.         standend();
  354.     if (scr_mode == ANSI) {
  355.         fflush(stdout);
  356.     }
  357.     /* Always turn off RAW mode -- it is unlikely that anybody
  358.        would want to interface to COMMAND.COM in raw mode.
  359.        This way, if you were accidentally left in RAW mode
  360.        because of a crash, it will go away if you re-enter. */
  361.     setraw(STDIN_HANDLE, No);
  362.     setbreak(wasbreak);
  363.  
  364.     started = No;
  365. #ifdef VTRMTRACE
  366.     if (vtrmfp) fclose(vtrmfp);
  367. #endif
  368. }
  369.  
  370. /*
  371.  * Set all internal statuses to undefined, especially the contents of
  372.  * the screen, so a hard redraw will not be optimised to heaven.
  373.  */
  374.  
  375. Visible Procedure trmundefined()
  376. {
  377.     register int y, x;
  378. #ifdef VTRMTRACE
  379.     if (vtrmfp) fprintf(vtrmfp, "\ttrmundefined();\n");
  380. #endif
  381.  
  382.     cur_y = cur_x = Undefined;
  383.     so_mode = Undefined;
  384.  
  385.     for (y = 0; y < lines; y++) {
  386.         for (x = 0; x < cols; x++) {
  387.             linedata[y][x] = ' ';
  388.             linemode[y][x] = PLAIN;
  389.         }
  390.             /* they may get printed in scrolling */
  391.         lenline[y] = cols;
  392.     }
  393. }
  394.  
  395. #ifdef VTRMTRACE
  396.  
  397. Hidden Procedure check_started(m)
  398.      char *m;
  399. {
  400.     if (!started) {
  401.         trmend();
  402.         if (vtrmfp) fprintf(vtrmfp, "Not started: %s\n", m);
  403.         exit(TE_TWICE);
  404.     }
  405. }
  406. #else
  407.  
  408. #define check_started(m) /*empty*/
  409.  
  410. #endif
  411.  
  412. /*
  413.  * Sensing the cursor.
  414.  * (NOT IMPLEMENTED, since there is no way to locally move the cursor.)
  415.  */
  416.  
  417. /*
  418.  * Sense the current (y, x) cursor position, after a possible manual
  419.  * change by the user with local cursor motions.
  420.  * If the terminal cannot be asked for the current cursor position,
  421.  * or if the string returned by the terminal is garbled,
  422.  * the position is made Undefined.
  423.  */
  424. Visible Procedure trmsense(sense, format, py, px)
  425.      string sense;
  426.      string format;
  427.      int *py;
  428.      int *px;
  429. {
  430. #ifdef VTRMTRACE
  431.     if (vtrmfp) fprintf(vtrmfp, "\ttrmsense(&yy, &xx);\n");
  432. #endif
  433.     check_started("trmsense called outside trmstart/trmend");
  434.  
  435. /*    *py = *px = Undefined;
  436.  *
  437.  *
  438.  *    if (flags&CAN_SENSE && getpos(py, px)) {
  439.  *        if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
  440.  *            *py = *px = Undefined;
  441.  *    }
  442.  */
  443.     *py = *px= Undefined;
  444. }
  445.  
  446. /*
  447.  * Putting data on the screen.
  448.  */
  449.  
  450. /*
  451.  * Fill screen area with given "data".
  452.  * Characters for which the corresponding chars in "mode" have the value
  453.  * STANDOUT must be put in inverse video.
  454.  */
  455. Visible Procedure trmputdata(yfirst, ylast, indent, data, mode)
  456.      int yfirst;
  457.      int ylast;
  458.      register int indent;
  459.      register string data;
  460.      string mode;
  461. {
  462.     register int y;
  463.     int x, len, lendata, space;
  464.  
  465. #ifdef VTRMTRACE
  466.     if (vtrmfp) fprintf(vtrmfp, "\ttrmputdata(%d, %d, %d, \"%s\");\n",
  467.                 yfirst, ylast, indent, data);
  468. #endif
  469.     check_started("trmputdata called outside trmstart/trmend");
  470.  
  471.     if (yfirst < 0)
  472.         yfirst = 0;
  473.     if (ylast >= lines)
  474.         ylast = lines-1;
  475.     space = cols*(ylast-yfirst+1) - indent;
  476.     if (space <= 0)
  477.         return;
  478.     yfirst += indent/cols;
  479.     indent %= cols;
  480.     y = yfirst;
  481.     if (data) {
  482.         x = indent;
  483.         lendata = strlen(data);
  484.         if (ylast == lines-1 && lendata >= space)
  485.             lendata = space - 1;
  486.         len = Min(lendata, cols-x);
  487.         while (/*** len > 0 && ***/ y <= ylast) {
  488.             put_line(y, x, data, mode, len);
  489.             y++;
  490.             lendata -= len;
  491.             if (lendata > 0) {
  492.                 x = 0;
  493.                 data += len;
  494.                 if (mode != NULL)
  495.                     mode += len;
  496.                 len = Min(lendata, cols);
  497.             }
  498.             else
  499.                 break;
  500.         }
  501.     }
  502.     if (y <= ylast)
  503.         clear_lines(y, ylast);
  504. }
  505.  
  506. /*
  507.  * We will try to get the picture:
  508.  *
  509.  *            op>>>>>>>>>>>op                       oq
  510.  *            ^         ^                       ^
  511.  *         <xskip><-----m1----><---------------od-------------------->
  512.  *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
  513.  *   NEW:       "in a maze of little twisting pieces of code, all alike"
  514.  *            <-----m1----><----------------nd--------------------->
  515.  *            ^         ^                     ^
  516.  *            np>>>>>>>>>>>np                     nq
  517.  * where
  518.  *    op, oq, np, nq are pointers to start and end of Old and New data,
  519.  * and
  520.  *    xskip = length of indent to be skipped,
  521.  *    m1 = length of Matching part at start,
  522.  *    od = length of Differing end on screen,
  523.  *    nd = length of Differing end in data to be put.
  524.  */
  525. Hidden int put_line(y, xskip, data, mode, len)
  526.      int y;
  527.      int xskip;
  528.      string data;
  529.      string mode;
  530.      int len;
  531. {
  532.     register char *op, *oq, *mp;
  533.     char *np, *nq, *mo;
  534.     int m1, od, nd, delta;
  535.  
  536.     /* Bugfix GvR 19-June-87: */
  537.     while (lenline[y] < xskip) {
  538.         linedata[y][lenline[y]] = ' ';
  539.         linemode[y][lenline[y]] = PLAIN;
  540.         lenline[y]++;
  541.     }
  542.     
  543.     /* calculate the magic parameters */
  544.     op = &linedata[y][xskip];
  545.     oq = &linedata[y][lenline[y]-1];
  546.     mp = &linemode[y][xskip];
  547.     np = data;
  548.     nq = data + len - 1;
  549.     mo = (mode != NULL ? mode : plain);
  550.     m1 = 0;
  551.     while (*op == *np && *mp == *mo && op <= oq && np <= nq) {
  552.         op++, np++, mp++, m1++;
  553.         if (mode != NULL) mo++;
  554.     }
  555.     od = oq - op + 1;
  556.     nd = nq - np + 1;
  557.     /* now we have the picture above */
  558.  
  559.     if (od==0 && nd==0)
  560.         return;
  561.  
  562.     delta = nd - od;
  563.     move(y, xskip + m1);
  564.     if (nd > 0) {
  565.         mo= (mode != NULL ? mode+(np-data) : NULL);
  566.         put_str(np, mo, nd);
  567.     }
  568.     if (delta < 0) {
  569.         clr_to_eol();
  570.         return;
  571.     }
  572.     lenline[y] = xskip + len;
  573.     if (cur_x == cols) {
  574.         cur_y++;
  575.         cur_x = 0;
  576.     }
  577. }
  578.  
  579. /*
  580.  * Scrolling (part of) the screen up (or down, dy<0).
  581.  */
  582.  
  583. Visible Procedure trmscrollup(yfirst, ylast, by)
  584.      register int yfirst;
  585.      register int ylast;
  586.      register int by;
  587. {
  588. #ifdef VTRMTRACE
  589.     if (vtrmfp) fprintf(vtrmfp, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by);
  590. #endif
  591.     check_started("trmscrollup called outside trmstart/trmend");
  592.  
  593.     if (by == 0)
  594.         return;
  595.  
  596.     if (yfirst < 0)
  597.         yfirst = 0;
  598.     if (ylast >= lines)
  599.         ylast = lines-1;
  600.  
  601.     if (yfirst > ylast)
  602.         return;
  603.  
  604.     if (so_mode != Off)
  605.         standend();
  606.  
  607.     if (by > 0 && yfirst + by > ylast
  608.         ||
  609.         by < 0 && yfirst - by > ylast)
  610.     {
  611.         clear_lines(yfirst, ylast);
  612.         return;
  613.     }
  614.  
  615.     switch (scr_mode) {
  616.     case BIOS:
  617.         biosscrollup(yfirst, ylast, by);
  618.         break;
  619.     case ANSI:
  620.         if (by > 0 && yfirst == 0) {
  621.             lf_scroll(ylast, by);
  622.         }
  623.         else if (by > 0) {
  624.             move_lines(yfirst+by, yfirst, ylast-yfirst+1-by, 1);
  625.             clear_lines(ylast-by+1, ylast);
  626.         }
  627.         else {
  628.             move_lines(ylast+by, ylast, ylast-yfirst+1+by, -1);
  629.             clear_lines(yfirst, yfirst-by-1);
  630.         }
  631.         break;
  632.     }
  633. }
  634.  
  635. /*
  636.  * Synchronization, move cursor to given position (or previous if < 0).
  637.  */
  638.  
  639. Visible Procedure trmsync(y, x)
  640.      int y;
  641.      int x;
  642. {
  643. #ifdef VTRMTRACE
  644.     if (vtrmfp) fprintf(vtrmfp, "\ttrmsync(%d, %d);\n", y, x);
  645. #endif
  646.     check_started("trmsync called outside trmstart/trmend");
  647.  
  648.     if (0 <= y && y < lines && 0 <= x && x < cols) {
  649.         move(y, x);
  650.     }
  651.     fflush(stdout);
  652. }
  653.  
  654. /*
  655.  * Send a bell, visible if possible.
  656.  */
  657.  
  658. Visible Procedure trmbell() {
  659. #ifdef VTRMTRACE
  660.     if (vtrmfp) fprintf(vtrmfp, "\ttrmbell();\n");
  661. #endif
  662.     check_started("trmbell called outside trmstart/trmend");
  663.     ring_bell();
  664. }
  665.  
  666. /*
  667.  * Now for the real work: here are all low level routines that really
  668.  * differ for BIOS or ANSI mode.
  669.  */
  670.  
  671. /*
  672.  * BIOS video io is called by generating an 8086 software interrupt,
  673.  * using lattice's int86() function.
  674.  * To ease coding, all routines fill in the apropriate parameters in regs,
  675.  * and then call bios10(code), where code is to be placed in ah.
  676.  */
  677.  
  678. Hidden union REGS regs, outregs;
  679.  
  680. /* A macro for speed  */
  681. #define bios10(code) (regs.h.ah = (code), int86(0x10, ®s, ®s))
  682. #define nbios10(code) (regs.h.ah = (code), int86(0x10, ®s, &outregs))
  683.  
  684. /* Video attributes: (see the BASIC manual) (used for standout mode) */
  685.  
  686. Hidden int video_attr;
  687. #define V_NORMAL 7
  688. #define V_STANDOUT (7<<4)
  689.  
  690. /* Some BIOS only routines */
  691.  
  692. Hidden get_cols()
  693. {
  694.     bios10(15);
  695.     return regs.h.ah;
  696. }
  697.  
  698. /*
  699.  * ANSI escape sequences
  700.  */
  701. #define A_CUP    "\033[%d;%dH"   /* cursor position */
  702. #define A_SGR0    "\033[0m"       /* set graphics rendition to normal */
  703. #define A_SGR7    "\033[7m"       /* set graphics rendition to standout */
  704. #define A_ED    "\033[2J"       /* erase display (and cursor home) */
  705. #define A_EL    "\033[K"        /* erase (to end of) line */
  706.  
  707. /*
  708.  * The following routine is the time bottleneck, I believe!
  709.  */
  710.  
  711. Hidden Procedure put_str(data, mode, n)
  712.      char *data;
  713.      char *mode;
  714.      int n;
  715. {
  716.     register char c, mo, so;
  717.  
  718.     so = so_mode;
  719.     if (scr_mode == BIOS) {
  720.         regs.x.cx = 1;    /* repition count */
  721.         regs.h.bh = 0;    /* page number */
  722.         regs.h.bl = video_attr;
  723.         while (--n >= 0) {
  724.             c = *data++;
  725.             mo= (mode != NULL ? *mode++ : PLAIN);
  726.             if (mo != so) {
  727.                 so= mo;
  728.                 so ? standout() : standend();
  729.                 regs.h.bl = video_attr;
  730.             }
  731.             regs.h.al = c /* &CHAR */;
  732.             nbios10(9);
  733.             if (cur_x >= cols-1) {
  734.                 linedata[cur_y][cols-1] = c;
  735.                 linemode[cur_y][cols-1] = mo;
  736.                 continue;
  737.             }
  738.             regs.h.dh = cur_y;
  739.             regs.h.dl = cur_x + 1;
  740.             nbios10(2);
  741.             linedata[cur_y][cur_x] = c;
  742.             linemode[cur_y][cur_x]= mo;
  743.             cur_x++;
  744.         }
  745.     }
  746.     else {
  747.         while (--n >= 0) {
  748.             c = *data++;
  749.             mo= (mode != NULL ? *mode++ : PLAIN);
  750.             if (mo != so) {
  751.                 so= mo;
  752.                 so ? standout() : standend();
  753.             }
  754.             putch(c);
  755.             linedata[cur_y][cur_x] = c;
  756.             linemode[cur_y][cur_x]= mo;
  757.             cur_x++;
  758.         }
  759.     }
  760. }
  761.  
  762. /*
  763.  * Move to position y,x on the screen
  764.  */
  765.  
  766. Hidden Procedure move(y, x)
  767.      int y;
  768.      int x;
  769. {
  770.     if (scr_mode != BIOS && cur_y == y && cur_x == x)
  771.         return;
  772.     switch (scr_mode) {
  773.     case BIOS:
  774.         regs.h.dh = y;
  775.         regs.h.dl = x;
  776.         regs.h.bh = 0; /* Page; must be 0 for graphics */
  777.         bios10(2);
  778.         break;
  779.     case ANSI:
  780.         cprintf(A_CUP, y+1, x+1);
  781.         break;
  782.     }
  783.     cur_y = y;
  784.     cur_x = x;
  785. }
  786.  
  787. Hidden Procedure standout()
  788. {
  789.     so_mode = On;
  790.     switch (scr_mode) {
  791.     case BIOS:
  792.         video_attr = V_STANDOUT;
  793.         break;
  794.     case ANSI:
  795.         cputs(A_SGR7);
  796.         break;
  797.     }
  798. }
  799.  
  800. Hidden Procedure standend()
  801. {
  802.     so_mode = Off;
  803.     switch (scr_mode) {
  804.     case BIOS:
  805.         video_attr = V_NORMAL;
  806.         break;
  807.     case ANSI:
  808.         cputs(A_SGR0);
  809.         break;
  810.     }
  811. }
  812.  
  813. Hidden Procedure clear_lines(yfirst, ylast)
  814.      int yfirst;
  815.      int ylast;
  816. {
  817.     register int y;
  818.  
  819.     if (scr_mode == BIOS) {
  820.         regs.h.al = 0;    /* scroll with al = 0 means blank window */
  821.         regs.h.ch = yfirst;
  822.         regs.h.cl = 0;
  823.         regs.h.dh = ylast;
  824.         regs.h.dl = cols-1;
  825.         regs.h.bh = V_NORMAL;
  826.         bios10(6);
  827.         for (y = yfirst; y <= ylast; y++)
  828.             lenline[y] = 0;
  829.         return;
  830.     }
  831.     /* scr_mode == ANSI */
  832.     if (yfirst == 0 && ylast == lines-1) {
  833.         if (so_mode == On)
  834.             standend();
  835.         move(0, 0);        /* since some ANSI'd don't move */
  836.         cputs(A_ED);
  837.         cur_y = cur_x = 0;
  838.         for (y = yfirst; y < ylast; y++)
  839.             lenline[y] = 0;
  840.         return;
  841.     }
  842.     for (y = yfirst; y <= ylast; y++) {
  843.         if (lenline[y] > 0) {
  844.             move(y, 0);
  845.             clr_to_eol();
  846.         }
  847.     }
  848. }
  849.  
  850. Hidden Procedure clr_to_eol()
  851. {
  852.     if (so_mode == On)
  853.         standend();
  854.     switch (scr_mode) {
  855.     case BIOS:
  856.         regs.h.bh = 0;    /* page */
  857.         regs.x.cx = lenline[cur_y] - cur_x;
  858.         regs.h.al = ' ';
  859.         regs.h.bl = V_NORMAL;
  860.         bios10(9);
  861.         break;
  862.     case ANSI:
  863.         cputs(A_EL);
  864.         break;
  865.     }
  866.     lenline[cur_y] = cur_x;
  867. }
  868.  
  869. /* scrolling for BIOS */
  870.  
  871. Hidden Procedure biosscrollup(yfirst, ylast, by)
  872.      int yfirst;
  873.      int ylast;
  874.      int by;
  875. {
  876.     regs.h.al = (by < 0 ? -by : by);
  877.     regs.h.ch = yfirst;
  878.     regs.h.cl = 0;
  879.     regs.h.dh = ylast;
  880.     regs.h.dl = cols-1;
  881.     regs.h.bh= V_NORMAL;
  882.     bios10(by < 0 ? 7 : 6);
  883.     cur_y = cur_x = Undefined;
  884.     if (by > 0)
  885.         scr_lines(yfirst, ylast, by, 1);
  886.     else
  887.         scr_lines(ylast, yfirst, -by, -1);
  888. }
  889.  
  890. /* Reset internal administration accordingly */
  891.  
  892. Hidden Procedure scr_lines(yfrom, yto, n, dy)
  893.      int yfrom;
  894.      int yto;
  895.      int n;
  896.      int dy;
  897. {
  898.     register int y, x;
  899.     char *savedata;
  900.     char *savemode;
  901.  
  902.     while (n-- > 0) {
  903.         savedata = linedata[yfrom];
  904.         savemode= linemode[yfrom];
  905.         for (y = yfrom; y != yto; y += dy) {
  906.             linedata[y] = linedata[y+dy];
  907.             linemode[y] = linemode[y+dy];
  908.             lenline[y] = lenline[y+dy];
  909.         }
  910.         linedata[yto] = savedata;
  911.         linemode[yto] = savemode;
  912.         for (x = 0; x < cols; x++ ) {
  913.             linedata[yto][x] = ' ';
  914.             linemode[yto][x] = PLAIN;
  915.         }
  916.         lenline[yto] = 0;
  917.     }
  918. }
  919.  
  920. Hidden Procedure lf_scroll(yto, by)
  921.      int yto;
  922.      int by;
  923. {
  924.     register int n = by;
  925.  
  926.     move(lines-1, 0);
  927.     while (n-- > 0) {
  928.         putch('\n');
  929.     }
  930.     scr_lines(0, lines-1, by, 1);
  931.     move_lines(lines-1-by, lines-1, lines-1-yto, -1);
  932.     clear_lines(yto-by+1, yto);
  933. }
  934.  
  935. /* for dumb scrolling, uses and updates internal administration */
  936.  
  937. Hidden Procedure move_lines(yfrom, yto, n, dy)
  938.      int yfrom;
  939.      int yto;
  940.      int n;
  941.      int dy;
  942. {
  943.     while (n-- > 0) {
  944.         put_line(yto, 0, linedata[yfrom], linemode[yfrom], lenline[yfrom]);
  945.         yfrom += dy;
  946.         yto += dy;
  947.     }
  948. }
  949.  
  950. Hidden Procedure ring_bell()
  951. {
  952.     switch (scr_mode) {
  953.     case BIOS:
  954.         regs.h.al = '\007';
  955.         regs.h.bl = V_NORMAL;
  956.         bios10(14);
  957.         break;
  958.     case ANSI:
  959.         putch('\007');
  960.         break;
  961.     }
  962. }
  963.  
  964. /*
  965.  * Show the current internal statuses of the screen on vtrmfp.
  966.  * For debugging only.
  967.  */
  968.  
  969. #ifdef SHOW
  970. Visible Procedure trmshow(s)
  971.      char *s;
  972. {
  973.     int y, x;
  974.  
  975.     if (!vtrmfp)
  976.         return;
  977.     fprintf(vtrmfp, "<<< %s >>>\n", s);
  978.     for (y = 0; y < lines; y++) {
  979.         for (x = 0; x <= lenline[y] /*** && x < cols ***/ ; x++) {
  980.             fputc(linedata[y][x], vtrmfp);
  981.         }
  982.         fputc('\n', vtrmfp);
  983.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  984.             if (linemode[y][x] == STANDOUT)
  985.                 fputc('-', vtrmfp);
  986.             else
  987.                 fputc(' ', vtrmfp);
  988.         }
  989.         fputc('\n', vtrmfp);
  990.     }
  991.     fprintf(vtrmfp, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
  992.     fflush(vtrmfp);
  993. }
  994. #endif
  995.  
  996. /*
  997.  * Interrupt handling.
  998.  *
  999.  * (This has not properly been tested, nor is it clear that
  1000.  * this interface is what we want.  Anyway, it's here for you
  1001.  * to experiment with.  What does it do, you may ask?
  1002.  * Assume an interactive program which reads its characters
  1003.  * through trminput.  Assume ^C is the interrupt character.
  1004.  * Normally, ^C is treated just like any other character: when
  1005.  * typed, it turns up in the input.  The program may understand
  1006.  * input ^C as "quit from the current mode".
  1007.  * Occasionally, the program goes into a long period of computation.
  1008.  * Now it would be uninterruptible, except if it calls a routine,
  1009.  * say pollinterrupt(), at times in its computational loop;
  1010.  * pollinterrupt() magically looks ahead in the input queue, 
  1011.  * via trmavail() and trminput(), and if it sees a ^C, discards all input
  1012.  * before that point and returns Yes.  It also sets a flag, so that
  1013.  * the interupt "sticks around" until either trminput or trmavail
  1014.  * is called.  It is undefined whether typing ^C several times in
  1015.  * a row is seen as one interrupt, or an interrupt followed by input
  1016.  * of ^C's.  A program should be prepared for either.)
  1017.  */
  1018.  
  1019. #ifdef NOT_USED
  1020.  
  1021. extern bool intrflag;
  1022.  
  1023. bool trminterrupt()
  1024. {
  1025.     /* Force a check for ^C which will call handler. */
  1026.     /* (This does a ^C check even if stdin is in RAW mode. */
  1027.     (void) kbhit();
  1028.     return intrflag;
  1029. }
  1030.  
  1031. #endif /* NOT_USED */
  1032.  
  1033.  
  1034. /* Definitions for DOS function calls. */
  1035.  
  1036. #define IOCTL        0x44
  1037. #define IOCTL_GETDATA    0x4400
  1038. #define IOCTL_SETDATA    0x4401
  1039. #define DEVICEBIT    0x80
  1040. #define RAWBIT        0x20
  1041.  
  1042. #define BREAKCK        0x33
  1043. #define GET        0x00
  1044. #define SET        0x01
  1045.  
  1046. #define IOCTL_GETSTS    0x4406
  1047.  
  1048. /*
  1049.  * Terminal input without echo.
  1050.  */
  1051.  
  1052. Visible int trminput()
  1053. {
  1054.     char c;
  1055.  
  1056. #ifdef NOT_USED
  1057.     intrflag= No;
  1058. #endif
  1059.     /* Assume stdin is in RAW mode; this turns echo and ^C checks off. */
  1060.     if (read(STDIN_HANDLE, &c, 1) < 1)
  1061.         return -1;
  1062.     else
  1063.         return c;
  1064. }
  1065.  
  1066. /*
  1067.  * Check for character available.
  1068.  *
  1069.  */
  1070.  
  1071. Visible bool trmavail()
  1072. {
  1073. #ifdef NOT_USED
  1074.     intrflag= No;
  1075. #endif
  1076.     regs.x.ax = IOCTL_GETSTS;
  1077.     regs.x.bx = STDIN_HANDLE;
  1078.     intdos(®s, ®s);
  1079.     if (regs.x.cflag)
  1080.         return -1; /* Error */
  1081.     return regs.h.al != 0;
  1082. }
  1083.  
  1084. /* Issue an IOCTL to turn RAW for a device on or off. */
  1085.  
  1086. Hidden Procedure setraw(handle, raw)
  1087.      int handle;
  1088.      bool raw;
  1089. {
  1090.     regs.x.ax = IOCTL_GETDATA;
  1091.     regs.x.bx = handle;
  1092.     intdos(®s, ®s);
  1093.     if (regs.x.cflag || !(regs.h.dl & DEVICEBIT))
  1094.         return; /* Error or not a device -- ignore it */
  1095.     regs.h.dh = 0;
  1096.     if (raw)
  1097.         regs.h.dl |= RAWBIT;
  1098.     else
  1099.         regs.h.dl &= ~RAWBIT;
  1100.     regs.x.ax = IOCTL_SETDATA;
  1101.     intdos(®s, ®s);
  1102.     /* Ignore errors */
  1103. }
  1104.  
  1105. /* Get the raw bit of a device. */
  1106.  
  1107. Hidden int getraw(handle)
  1108.      int handle;
  1109. {
  1110.     regs.x.ax = IOCTL_GETDATA;
  1111.     regs.x.bx = handle;
  1112.     intdos(®s, ®s);
  1113.     return !regs.x.cflag &&
  1114.         (regs.h.dh & (DEVICEBIT|RAWBIT)) == (DEVICEBIT|RAWBIT);
  1115. }
  1116.  
  1117. /* Set the break status. */
  1118.  
  1119. Hidden Procedure setbreak(on)
  1120.      bool on;
  1121. {
  1122.     bdos(BREAKCK, on, SET);
  1123. }
  1124.  
  1125. /* Get the break status. */
  1126.  
  1127. Hidden int getbreak()
  1128. {
  1129.     regs.x.ax = (BREAKCK << 8) | GET;
  1130.     intdos(®s, ®s);
  1131.     return regs.h.dl;
  1132. }
  1133.  
  1134. Visible int trmsuspend()
  1135. {
  1136.     return spawnlp(P_WAIT, "COMMAND.COM", NULL) == 0;
  1137. }
  1138.